﻿namespace Resharper.Abstractions.CodeDom.TypeMembers.Methods
{
  using System.Collections.Generic;
  using System.Linq;
  using JetBrains.Annotations;
  using JetBrains.ReSharper.Psi.CSharp.Tree;
  using Resharper.Abstractions.CodeDom.Statements.Blocks;
  using Resharper.Abstractions.CodeDom.TreeNodes;
  using Resharper.Abstractions.CodeDom.TypeMembers.Functions;

  /// <summary>Defines the <see cref="Method"/> class.</summary>
  public class Method : TypeMember, IFunction
  {
    #region Constructors and Destructors

    /// <summary>Initializes a new instance of the <see cref="Method"/> class.</summary>
    /// <param name="method">The function.</param>
    internal Method([NotNull] IMethodDeclaration method) : base(method)
    {
    }

    #endregion

    #region Implemented Interfaces

    #region IFunction

    /// <summary>
    /// Gets the body.
    /// </summary>
    public Block Body
    {
      get
      {
        var body = this.InternalMethod.Body;

        return body != null ? new Block(body) : null;
      }

      set
      {
        if (value == null)
        {
          this.InternalMethod.SetBody(null);
          return;
        }

        this.InternalMethod.SetBody(value.InternalBlock);
      }
    }

    /// <summary>
    /// Gets the type.
    /// </summary>
    public Type ReturnType
    {
      get
      {
        var declaredElement = this.InternalMethod.DeclaredElement;
        if (declaredElement == null)
        {
          return null;
        }

        var returnType = declaredElement.ReturnType;

        return new Type(returnType, this.InternalMethod.Language);
      }
    }

    #endregion

    #region IParametersOwner

    /// <summary>
    /// Gets or sets the parameters.
    /// </summary>
    /// <value>The parameters.</value>
    [NotNull]
    public IEnumerable<Parameter> Parameters
    {
      get
      {
        var declarations = this.InternalMethod.ParameterDeclarations;

        return declarations.Select(p => new Parameter(p));
      }
    }

    #endregion

    #endregion

    #region Properties

    /// <summary>
    /// Gets or sets the internal function.
    /// </summary>
    /// <value>The internal function.</value>
    [NotNull]
    internal IMethodDeclaration InternalMethod
    {
      get
      {
        return (IMethodDeclaration)this.InternalTreeNode;
      }
    }

    #endregion

    #region Public Methods

    /// <summary>Gets the method.</summary>
    /// <param name="treeNode">The tree node.</param>
    public static Method GetMethod(object treeNode)
    {
      var method = treeNode as IMethodDeclaration;

      return method != null ? new Method(method) : null;
    }

    /// <summary>
    /// Gets the name range.
    /// </summary>
    public Range NameRange
    {
      get
      {
        var r = this.InternalMethod.NameIdentifier.GetNavigationRange().TextRange;
        return new Range(r.StartOffset, r.EndOffset);
      }
    }

    #endregion
  }
}

